9 données pour dataviz¶

In [190]:
import pandas as pd
import requests
import geopandas as gpd
import os
from bs4 import BeautifulSoup
In [2]:
def download_file(url, filename):
    if filename in os.listdir():
        print("Fichier déjà présent")
        return None
    response = requests.get(url)
    with open(filename, 'wb') as f:
        f.write(response.content)
    print('Fichier téléchargé')
In [3]:
def get_df(url, filename, encoding='utf-8'):
    download_file(url, filename)
    if filename.endswith('csv'):
        return pd.read_csv(filename, encoding=encoding, sep=';')
    elif filename.endswith('geojson'):
        return gpd.read_file(filename)
In [4]:
communes = [
    "Dardilly",
    "Lyon",
    "Villeurbanne",
    "Saint-Genis-Laval",
    "Caluire-et-Cuire",
    "Vaulx-en-Velin",
    "Écully",
    "Rillieux-la-Pape",
    "Saint-Didier-au-Mont-d'Or",
]
In [5]:
len(communes)
Out[5]:
9

démographie¶

In [6]:
### INsee mais c'est tout pété leur système d'inscription

équipements sportifs¶

In [7]:
equipements_url = "https://download.data.grandlyon.com/wfs/rdata?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=urbalyon.recenseqptsport&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
equipements_filename = "equipements_sportifs_urba_lyon.geojson"
In [8]:
es = get_df(equipements_url, equipements_filename)
Fichier déjà présent
In [9]:
es_columns = ['nom', 'type', 'categorie', 'installation', 'commune', 'geometry',]
es = es[es_columns]
In [10]:
# remplacer Lyon Xème par Lyon
es.commune = es.commune.apply(lambda s: "Lyon" if s.startswith('Lyon') else s)
In [11]:
# filtrer sur les communes du comité
es = es[es.commune.isin(communes)]
In [12]:
es.head()
Out[12]:
nom type categorie installation commune geometry
36 Hall de tennis Court de tennis Courts de tennis Stade Henri Cochet/Fitness Parc Caluire-et-Cuire POINT (4.83777 45.79185)
37 salle de musculation Salle de musculation/cardiotraining Salles de pratiques collectives You Can Fit Caluire-et-Cuire POINT (4.83409 45.78693)
38 Les quais de saône Boucle de randonnée Nature Circuits de Randonnees Caluire-et-Cuire POINT (4.83646 45.78780)
39 aviron club de lyon caluire Site d'activités aquatiques et nautiques Nature Aviron Club de Lyon Caluire Caluire-et-Cuire POINT (4.82767 45.79466)
40 SALLE DE BOXE BATAG Salle de boxe Salles de pratiques collectives Salle de Boxe Batag Vaulx-en-Velin POINT (4.91805 45.78573)
In [13]:
# compter par commune 
es.groupby('commune').count()[['nom']]
Out[13]:
nom
commune
Caluire-et-Cuire 71
Dardilly 52
Lyon 863
Rillieux-la-Pape 90
Saint-Didier-au-Mont-d'Or 18
Saint-Genis-Laval 46
Vaulx-en-Velin 127
Villeurbanne 288
Écully 77
In [14]:
es.explore(column='commune', tiles="Stamen Toner")
Out[14]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Parcs et jardins¶

In [312]:
parcs_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=com_donnees_communales.comparcjardin_1_0_0&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
parcs_filename = "parcs_metropole.geojson"
parcs = get_df(parcs_url, parcs_filename)
Fichier déjà présent

parcs['surface_m2'] = parcs['geometry'].to_crs({'init': 'epsg:3395'}).map(lambda p: p.area)

In [340]:
parcs[['commune', "nom", "surf_tot_m2",'geometry']].explore(tiles='Stamen Toner', column='commune')
Out[340]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [324]:
parcs_surface = parcs.groupby('commune').sum(numeric_only=True)[['surf_tot_m2']]
In [327]:
parcs_surface['surface_km2'] = parcs_surface.surf_tot_m2 / 10**6
In [328]:
parcs_surface[parcs_surface.index.isin(communes)]
Out[328]:
surf_tot_m2 surface_km2
commune
Lyon 3.842108e+06 3.842108
Rillieux-la-Pape 9.539670e+05 0.953967
Saint-Didier-au-Mont-d'Or 2.361500e+04 0.023615
Saint-Genis-Laval 1.063895e+05 0.106390
Vaulx-en-Velin 6.351486e+06 6.351486
Villeurbanne 6.557329e+05 0.655733
In [334]:
parcs_nombres = parcs.groupby('commune').count()[['nom']]
In [337]:
parcs_nombres[parcs_nombres.index.isin(communes)]
Out[337]:
nom
commune
Lyon 296
Rillieux-la-Pape 15
Saint-Didier-au-Mont-d'Or 2
Saint-Genis-Laval 4
Vaulx-en-Velin 35
Villeurbanne 71

équipements culturels¶

In [ ]:
 

bureaux de vote¶

In [112]:
bdv_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=ter_territoire.terbureauvote&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_filename = "bdv.geojson"
bdv = get_df(bdv_url, bdv_filename)
Fichier déjà présent
In [113]:
# remplacer Lyon Nème par Lyon
bdv.commune = bdv.commune.apply(lambda s: 'Lyon' if s.startswith('Lyon') else s)
In [114]:
bdv_ecully_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=ecully.bureauvote&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_ecully_filename = "bdv_ecully.geojson"
In [115]:
bdv_ecully = get_df(bdv_ecully_url, bdv_ecully_filename)
Fichier déjà présent
In [116]:
# fix le nom de Écully dans ce fichier pour qu'il match avec la liste des communes
bdv_ecully.commune = bdv_ecully.commune.str.replace('E', 'É')
In [117]:
communes
Out[117]:
['Dardilly',
 'Lyon',
 'Villeurbanne',
 'Saint-Genis-Laval',
 'Caluire-et-Cuire',
 'Vaulx-en-Velin',
 'Écully',
 'Rillieux-la-Pape',
 "Saint-Didier-au-Mont-d'Or"]
In [118]:
bdv_vaulx_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=vaulx.bureauvote_latest&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_vaulx_filename = "bdv_vaulx.geojson"
In [119]:
bdv_vaulx = get_df(bdv_vaulx_url, bdv_vaulx_filename)
Fichier déjà présent
In [120]:
bdv_dardilly_url = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=dardilly.bureauvote&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
bdv_dardilly_filename = "bdv_dardilly.geojson"
bdv_dardilly = get_df(bdv_dardilly_url, bdv_dardilly_filename)
Fichier déjà présent
In [121]:
# on empile les fichiers de Écully, Vaulx-en-Velin et Dardilly dans le fichier général bdv
bdv = pd.concat([bdv, bdv_ecully])
bdv = pd.concat([bdv, bdv_vaulx])
bdv = pd.concat([bdv, bdv_dardilly])
In [127]:
# on ne garde que les communes du comité de suivi
bdv = bdv[bdv.commune.isin(communes)]
In [128]:
bdv.explore(column='commune', tiles="Stamen Toner")
Out[128]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [129]:
bdv.commune.value_counts()
Out[129]:
Lyon                         304
Villeurbanne                  80
Caluire-et-Cuire              37
Vaulx-en-Velin                20
Rillieux-la-Pape              18
Saint-Genis-Laval             14
Écully                        11
Saint-Didier-au-Mont-d'Or      6
Dardilly                       6
Name: commune, dtype: int64

Nombres d'adresses dans la BAN¶

In [257]:
codes_insee_url = "https://download.data.grandlyon.com/ws/grandlyon/adr_voie_lieu.adrcomgl/all.csv?maxfeatures=-1"
codes_insee_filename = "communes.csv"
In [258]:
codes = get_df(codes_insee_url, codes_insee_filename)
Fichier déjà présent
In [259]:
codes = codes[codes.nom.isin(communes)].reset_index()[['nom', 'insee']]
In [260]:
codes
Out[260]:
nom insee
0 Dardilly 69072
1 Rillieux-la-Pape 69286
2 Lyon 69123
3 Vaulx-en-Velin 69256
4 Écully 69081
5 Villeurbanne 69266
6 Saint-Genis-Laval 69204
7 Saint-Didier-au-Mont-d'Or 69194
8 Caluire-et-Cuire 69034
In [261]:
# on ajoute les codes INSEE des arrondissements de Lyon
codes_lyon = pd.DataFrame([{"nom": "Lyon 1er Arrondissement" , "insee": 69381},
{"nom": "Lyon 2e Arrondissement", "insee": 69382},
{"nom": "Lyon 3e Arrondissement", "insee": 69383},
{"nom": "Lyon 4e Arrondissement", "insee": 69384},
{"nom": "Lyon 5e Arrondissement", "insee": 69385},
{"nom": "Lyon 6e Arrondissement", "insee": 69386},
{"nom": "Lyon 7e Arrondissement", "insee": 69387},
{"nom": "Lyon 8e Arrondissement", "insee": 69388},
{"nom": "Lyon 9e Arrondissement", "insee": 69389},
])

codes = pd.concat((codes, codes_lyon))

# on retire Lyon tout court pour éviter une 404 sur le site qu'on va scrapper 
codes = codes[codes.nom != 'Lyon']
In [262]:
# on scrappe sur le site adresse.data.gouv.fr parce que je ne vois pas d'api pour avoir le nombre d'adresse par commune
base_url = "https://adresse.data.gouv.fr/commune/"
adresses = []

for row in codes.itertuples():
    print(row.nom)
    commune_url = base_url + str(row.insee)
    response = requests.get(commune_url)
    soup = BeautifulSoup(response.content)
    nb_adresses = int(soup.find_all('div', class_="jsx-1548534320")[7].text)
    adresses.append(nb_adresses)
Dardilly
Rillieux-la-Pape
Vaulx-en-Velin
Écully
Villeurbanne
Saint-Genis-Laval
Saint-Didier-au-Mont-d'Or
Caluire-et-Cuire
Lyon 1er Arrondissement
Lyon 2e Arrondissement
Lyon 3e Arrondissement
Lyon 4e Arrondissement
Lyon 5e Arrondissement
Lyon 6e Arrondissement
Lyon 7e Arrondissement
Lyon 8e Arrondissement
Lyon 9e Arrondissement
In [263]:
codes['adresses'] = adresses
In [268]:
adr_lyon = codes[codes.nom.str.startswith('Lyon')].adresses.sum()
In [273]:
lyon = pd.DataFrame([{'nom': 'Lyon', 'insee': '69123', 'adresses': adr_lyon}])
In [278]:
nb_adresses = pd.concat((codes, lyon)).reset_index().drop(columns='index')
nb_adresses
Out[278]:
nom insee adresses
0 Dardilly 69072 2080
1 Rillieux-la-Pape 69286 3892
2 Vaulx-en-Velin 69256 4966
3 Écully 69081 1886
4 Villeurbanne 69266 11816
5 Saint-Genis-Laval 69204 3759
6 Saint-Didier-au-Mont-d'Or 69194 1342
7 Caluire-et-Cuire 69034 4616
8 Lyon 1er Arrondissement 69381 2029
9 Lyon 2e Arrondissement 69382 2612
10 Lyon 3e Arrondissement 69383 6780
11 Lyon 4e Arrondissement 69384 2213
12 Lyon 5e Arrondissement 69385 3324
13 Lyon 6e Arrondissement 69386 2993
14 Lyon 7e Arrondissement 69387 4323
15 Lyon 8e Arrondissement 69388 5034
16 Lyon 9e Arrondissement 69389 3423
17 Lyon 69123 32731

entreprises domicilées / par type¶

In [19]:
### INsee mais c'est tout pété leur système d'inscription
In [ ]:
 

contours des communes¶

In [20]:
contours = gpd.read_file('communes.geojson')
contours = contours[contours.nom.isin(communes)][["nom", "geometry"]]
In [21]:
contours
Out[21]:
nom geometry
318 Lyon POLYGON ((4.81349 45.74776, 4.80244 45.75172, ...
10475 Vaulx-en-Velin POLYGON ((4.91759 45.74829, 4.91898 45.75168, ...
11861 Villeurbanne POLYGON ((4.89901 45.75245, 4.89368 45.75380, ...
12075 Saint-Genis-Laval POLYGON ((4.82058 45.69558, 4.81750 45.69263, ...
12091 Rillieux-la-Pape POLYGON ((4.87981 45.79532, 4.87744 45.79697, ...
12240 Caluire-et-Cuire POLYGON ((4.84148 45.80344, 4.84325 45.80952, ...
12258 Dardilly POLYGON ((4.73602 45.83738, 4.73803 45.83788, ...
12262 Écully POLYGON ((4.78867 45.78953, 4.78577 45.78871, ...
12313 Saint-Didier-au-Mont-d'Or POLYGON ((4.78226 45.81140, 4.78416 45.81371, ...
In [22]:
contours.explore(tiles = None)
Out[22]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [23]:
os.makedirs('contours', exist_ok=True)
In [24]:
communes[-1] = communes[-1].replace("'", "’")
In [25]:
communes
Out[25]:
['Dardilly',
 'Lyon',
 'Villeurbanne',
 'Saint-Genis-Laval',
 'Caluire-et-Cuire',
 'Vaulx-en-Velin',
 'Écully',
 'Rillieux-la-Pape',
 'Saint-Didier-au-Mont-d’Or']
In [26]:
contours.iloc[-1, 0] = communes[-1]
In [27]:
contours
Out[27]:
nom geometry
318 Lyon POLYGON ((4.81349 45.74776, 4.80244 45.75172, ...
10475 Vaulx-en-Velin POLYGON ((4.91759 45.74829, 4.91898 45.75168, ...
11861 Villeurbanne POLYGON ((4.89901 45.75245, 4.89368 45.75380, ...
12075 Saint-Genis-Laval POLYGON ((4.82058 45.69558, 4.81750 45.69263, ...
12091 Rillieux-la-Pape POLYGON ((4.87981 45.79532, 4.87744 45.79697, ...
12240 Caluire-et-Cuire POLYGON ((4.84148 45.80344, 4.84325 45.80952, ...
12258 Dardilly POLYGON ((4.73602 45.83738, 4.73803 45.83788, ...
12262 Écully POLYGON ((4.78867 45.78953, 4.78577 45.78871, ...
12313 Saint-Didier-au-Mont-d’Or POLYGON ((4.78226 45.81140, 4.78416 45.81371, ...
In [28]:
# export des contours en SVG pour chaque commune
for city in communes:
    filename = f'contours/{city}.shp'
    print(filename)
    contours[contours.nom == city].to_file(filename)
    cmd = f"""svgis draw {filename} -o contours/{city}.svg \
    --crs "+proj=aea +lat_1=20 +lat_2=60 +lat_0=40 \
    +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs"
    """
    os.system(cmd)
contours/Dardilly.shp
contours/Lyon.shp
contours/Villeurbanne.shp
contours/Saint-Genis-Laval.shp
contours/Caluire-et-Cuire.shp
contours/Vaulx-en-Velin.shp
contours/Écully.shp
contours/Rillieux-la-Pape.shp
contours/Saint-Didier-au-Mont-d’Or.shp

Nettoyage des fichiers exportés non SVG¶

In [29]:
for file in os.listdir('contours'):
    if not file.endswith('svg'):
        os.unlink(f"contours/{file}")
In [30]:
adresse_sharepoint = "https://tubalyon.sharepoint.com/Documents%20partages/Forms/AllItems.aspx?viewpath=%2FDocuments%20partages%2FForms%2FAllItems%2Easpx&id=%2FDocuments%20partages%2FPROJETS%2FEN%20COURS%2FOPEN%20DATA%20COMMUNES%2F2022%2D2023%2FCOMIT%C3%89%20DE%20SUIVI%2FCONTOURS%5FSVG&viewid=62afc8d0%2D8661%2D48ee%2Da1f1%2Dd31a543b6fa8"
In [31]:
adresse_sharepoint
Out[31]:
'https://tubalyon.sharepoint.com/Documents%20partages/Forms/AllItems.aspx?viewpath=%2FDocuments%20partages%2FForms%2FAllItems%2Easpx&id=%2FDocuments%20partages%2FPROJETS%2FEN%20COURS%2FOPEN%20DATA%20COMMUNES%2F2022%2D2023%2FCOMIT%C3%89%20DE%20SUIVI%2FCONTOURS%5FSVG&viewid=62afc8d0%2D8661%2D48ee%2Da1f1%2Dd31a543b6fa8'
In [ ]:
 

établissements scolaires¶

In [34]:
communes[-1] = "Saint-Didier-au-Mont-d'Or"
In [37]:
## établissement recevant du public
erp_url = "https://download.data.grandlyon.com/wfs/rdata?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=sdmis.erp&outputFormat=application/json; subtype=geojson&SRSNAME=EPSG:4171"
erp_filename = "erp.geojson"
In [38]:
erp = get_df(erp_url, erp_filename)
Fichier déjà présent
In [39]:
erp.commune = erp.commune.apply(lambda s: 'LYON' if s.startswith('LYON') else s)
In [40]:
COMMUNES = [commune.replace('-', " ").upper() for commune in communes]
In [41]:
COMMUNES
Out[41]:
['DARDILLY',
 'LYON',
 'VILLEURBANNE',
 'SAINT GENIS LAVAL',
 'CALUIRE ET CUIRE',
 'VAULX EN VELIN',
 'ÉCULLY',
 'RILLIEUX LA PAPE',
 "SAINT DIDIER AU MONT D'OR"]
In [42]:
# on filtre sur nos communes
erp = erp[erp.commune.isin(COMMUNES)]
In [279]:
ecoles = erp[(erp.libelle.str.startswith('Lyc')) | erp.libelle.str.contains('aternelle') | erp.libelle.str.contains('ollège') | erp.libelle.str.contains('primaire')  | erp.libelle.str.contains('élémentaire') | erp.libelle.str.contains('GS') ]
In [281]:
erp[erp.libelle.str.contains('ourette')]
Out[281]:
code libelle nature categorie type types_secondaires hebergement effectif_total_reglementaire adresse code_postal insee commune gid geometry
3963 E38100068000 Collège du site de la Tourette ERP 2 R 800 80 boulevard de la Croix-Rousse 69001 69381 LYON 3959 POINT (4.82457 45.77339)
In [286]:
ecoles.explore(color="purple", tiles="Stamen Toner")
Out[286]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [287]:
ecoles.commune.value_counts()
Out[287]:
LYON                         276
VILLEURBANNE                  66
VAULX EN VELIN                37
RILLIEUX LA PAPE              24
CALUIRE ET CUIRE              23
SAINT GENIS LAVAL             19
DARDILLY                      14
ÉCULLY                        14
SAINT DIDIER AU MONT D'OR      3
Name: commune, dtype: int64
In [ ]: